PKTDRVR Interface for Turbo Pascal 7.0 Version 1.0 Written 1993 by Oliver Rehmann (released to public domain) What is PKTDRVR ? ================= PKTDRVR is a unit for Turbo Pascal 7.0. It provides an object oriented interface for crynrware packet drivers. The Object ========== TPKTDRVR = OBJECT(TOBJECT) private { member variables } pktInt : Integer; pktHandle : Integer; pktRecvHandler : Pointer; pktStatus : TPKTSTATUS; pktError : Byte; pktRegs : Registers; pktAccessInfo : TACCESSTYPE; PROCEDURE TestForPktDriver; public { member functions } CONSTRUCTOR Init(IntNo : Integer); DESTRUCTOR Done; VIRTUAL; PROCEDURE ScanForPktDriver; FUNCTION GetStatus : TPKTSTATUS; FUNCTION GetError : Byte; FUNCTION GetHandle : Word; PROCEDURE GetAccessType (VAR pktAccessType : TACCESSTYPE); PROCEDURE DriverInfo (VAR pktInfo : TDRVRINFO ); PROCEDURE AccessType (VAR pktAccessType : TACCESSTYPE); PROCEDURE ReleaseType; PROCEDURE TerminateDriver; PROCEDURE GetAddress (Buffer : Pointer;BufLen : Word; VAR BufCopied : Word); PROCEDURE ResetInterface; PROCEDURE GetParameters (VAR pktParams : TPKTPARAMS); PROCEDURE SendPkt (Buffer : Pointer;BufLen : Word ); PROCEDURE As_SendPkt (Buffer : Pointer;BufLen : Word;Upcall : Pointer ); PROCEDURE SetRCVmode (Mode : Word); FUNCTION GetRCVmode : Word; PROCEDURE SetMulticastList(VAR mcList : Pointer; VAR mcLen : Word); PROCEDURE GetMulticastList(VAR mcList : Pointer; VAR mcLen : Word); PROCEDURE GetStatistics (VAR pktStatistics : TSTATISTICS ); PROCEDURE SetAddress (Address : Pointer; VAR AddrLen : Word); END; The object provides all functions found in the packet driver specification 1.09 from FTP. The functions will not be explained here. If more information is needed, consult the file PACKET_D.109 delivered with Crynrware packet drivers. The most important advantage is that you don't have to pass the handle and the packet driver interrupt to the different functions. These variable are member of the object TPKTDRVR. Sending and receiving packets ============================= IEEE 802.3 (Ethernet) Frame ÚÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄ¿ ³ 7 Bytes ³ 1 Byte ³ 6 Bytes ³ 6 Bytes ³ 2 Bytes ³ 46...1500 Bytes ³ 4 Bytes ³ ³ Preamble ³ SFD ³ Dest. ³ Source ³ Length ³ of Data ³ FCS ³ ³ ³ ³ Address ³ Address ³ (Type) ³ (at least 46 B) ³ ³ ÀÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÙ ³ ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ That is what you have to send. That is what you receive. If you send a packet (SendPkt-Function,As_SendPkt-Function) you have to pass a buffer to the function. BUFFER ------ 0 6 12 14 ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄ´...ÃÄÄÄÄÄÄ¿ ³ 6 Bytes dest. addr. ³ 6 Bytes source addr. ³ Length (Type) ³ Da...ta ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄ´...ÃÄÄÄÄÄÄÙ The Length of an Ethernet packet -------------------------------- The maximum length of an ethernet packet is 1518 bytes 6 bytes (dest) + 6 bytes (src) + 2 bytes (length) + 46-1500 bytes (data) + 4 bytes (FCS) = 1518 bytes This is because every station must be able to detect a collision on the network. (Defined for a network with 2500 meters; 5 segments of 500 meters connected to each other with four repeaters) A packet must be at least 64 bytes which means 46 or more bytes of data. If your data packet is smaller than 46 bytes you have to fill it up with dummy bytes. The following record definition is my favourite data structure to store packets received from packet driver. CONST EthernetPacketLen = 1520; TYPE TPacketBuffer = RECORD DataLen : Word; { Contents of register CX } Data : Array[00..EthernetPacketLen-1] of Byte; END; The receiver ============ When receiving frames using packet drivers you have to write a receiver function. For each arriving frame this function is called twice. First, the packet driver calls the function to get a pointer to a free memory block. The second call signals completion; that means the frame has been copied to your buffer. 1. Packet driver calls your function with AX = 0. AX = 0 CX = Size of frame including dest. address, source address and type field. That means size of data + 18 Bytes. - You have to pass back a pointer in ES:DI to the packet driver if you want to grab the packet otherwise ES:DI must be set to 0000:0000. ATTENTION ! Because you are in an interrupt (IRQ of LAN Adapter) you can't call any DOS-Functions (Int 21h) !!!! So allocate memory when your program starts. An array as an example. 2. Packet driver calls your function with AX = 1 AX = 1 - This signals completion. Packet driver has copied the frame including the header (6+6+2 Bytes) to your buffer. Make sure your receiver doesn't perform any timeconsuming work ! Example of a receiver --------------------- VAR LastFrame : Array[00..1520] of Byte; { Buffer for arriving frames } FrameCount : Word; { Flag to signal if buffer is occupied } FrameLength : Word; { Length of the last frame arrived } {$S-}PROCEDURE pktReceiver; ASSEMBLER; ASM PUSH AX { Push registers onto stack } PUSH BX PUSH CX PUSH DX CMP AX,0001 { AX=1 means frame copied } JZ @@FrameCopied CMP AX,0000 { AX=0 means allocate memory please } JZ @@AllocMemory JMP @@EXIT { Invalid register contents for AX so exit} @@AllocMemory: MOV DX,0 { ES:DI = 0000:0000, we don't want the packet } MOV ES,DX MOV DI,0 { We don't grab the packet } MOV DX,SEG FrameCount { Set correct data segment } MOV DS,DX MOV DX,FrameCount { Check if buffer is free (FrameCount = 0) } CMP DX,0 JNZ @@Exit { buffer is not free ! So exit with ES:DI = 0000:0000 } MOV DX,SEG LastFrame { Return a valid address for storage to the } MOV ES,DX { packet driver. ES:DI = Seg(LasFrame):Ofs(LastFrame) } MOV DI,OFFSET LastFrame MOV DX,SEG FrameLength MOV DS,DX MOV SI,OFFSET FrameLength MOV WORD PTR DS:[SI],CX { Store length of frame in FrameLength } JMP @@Exit @@FrameCopied: MOV DX,SEG FrameCount { Set correct data segment } MOV DS,DX MOV FrameCount,1 { Set Flag to 1 } { Now our buffer is occupied (FrameCount = 1). Our receiver will not accept any packets unless FrameCount = 0. } @@Exit: POP DX { Pop registers from stack } POP CX POP BX POP AX END; {$S+} I know it is possible to shorten the receiver by not setting the DS register every time when accessing a variable, but I have written receivers which really are much more complex than this one. I could have saved much time and many REBOOTS if I had done this from the beginning. (:-) PROCEDURE ProcessFrame; BEGIN { Do something with the frame } . . . FrameCount := 0; { Reset Flag. Now the receiver kann grab the next frame } END; Initializing the packet driver ============================== There are some important steps you have to do before the packet driver is ready to send and receive packets. VAR pktDriver : TPKTDRVR; { Declare an instance of the packet driver object. } pktDriverInfo : TDRVRINFO; { Declare a record for retrieving information from the driver. } pktDriverAccess: TACCESSTYPE; { Declare a record for accessing the driver. } PROCEDURE InitPktDriver; BEGIN WriteLn('Initializing packet driver....'); IF (pktDriver.GetStatus <> INITIALIZED) THEN BEGIN WriteLn('Could not initialze packet driver...'); WriteLn('Aborting...'); Halt($FF); END ELSE BEGIN { Packet driver found. Show some information } WriteLn('Packet driver found:'); pktDriver.DriverInfo(pktDriverInfo); WriteLn('Name = ',StrPas(pktDriverInfo.PName)); WriteLn('Version = ',pktDriverInfo.Version); WriteLn('IF-Type = ',pktDriverInfo.Type_); Write ('Func = '); CASE pktDriverInfo.Functionality OF 01 : WriteLn('Basic functions present.'); 02 : WriteLn('Basic & extended functions present.'); 05 : WriteLn('Basic & high-performance functions present.'); 06 : WriteLn('Basic, high-performance & extended functions present.'); END; END; { Fill in information used for accessing packet driver } WITH pktDriverAccess DO BEGIN if_class := pktDriverInfo.Class; if_type := ANYTYPE; if_number := 0; type_ := @TypeField; typelen := 0; receiver := @pktReceiver; { receiver procedure } END; { Access packet driver } pktDriver.AccessType(pktDriverAccess); WriteLn('Handle = ',pktDriver.GetHandle); WriteLn; { Setting packet driver to promiscuous mode Do the following step only if you want to grab all the packets on your network. Attention ! There can be heavy traffic on an Ethernet ! Be prepared for a hughr amount of frames arriving. } pktDriver.SetRCVmode(6); END; PROCEDURE TerminatePktDriver; BEGIN pktDriver.ReleaseType; END; { Main program entry } BEGIN InitPktDriver; REPEAT IF (FrameCount = 1) THEN ProcessFrame; UNTIL KeyPressed; TerminatePktDriver; END. For more programming details see the examples TRAFGEN and TRAFMON enclosed in this package. For more details on MAC (Medium Access Control) layer problems see the file MAC.TXT enclosed in this packet. Conditions ========== This packet is released to public domain. You can give copies to your friends or place it on BBSes as long as it remains in its original state. If you write comercial software based on this interface you are requested to duplicate the headers and the Copyright indications. The author can not be held responsible for any damages resulting from the use of this software ! The author can be reached through the following e-mail adresses: Internet : 100016.732@compuserve.com CZ8OR@zcvx00.decnet.ascom.ch CompuServe: ID: 100016,732